#include "ThingpleAbstractAPIClient.h"

ThingpleAbstractAPIClient::ThingpleAbstractAPIClient(const QString &client_id, const QString &client_secret)
    :_hostname("localhost"),_port(80),_path("/thingple/api"),_client_id(client_id),_client_secret(client_secret),_reader_name("FillingLineTerminal")
{
    srand(time(NULL));
}

ThingpleAbstractAPIClient::ThingpleAbstractAPIClient(const ThingpleAbstractAPIClient& source)
    :_hostname(source._hostname),_port(source._port),_path(source._path),_client_id(source._client_id),_client_secret(source._client_secret),_reader_name(source._reader_name)
{
    srand(time(NULL));
}

ThingpleAbstractAPIClient& ThingpleAbstractAPIClient::operator=(const ThingpleAbstractAPIClient& other)
{
    _hostname = other._hostname;
    _port = other._port;
    _path = other._path;
    _client_id = other._client_id;
    _client_secret = other._client_secret;
    _reader_name = other._reader_name;
    return *this;
}

void ThingpleAbstractAPIClient::setHostname(const QString hostname)
{
    _hostname = hostname;
}

void ThingpleAbstractAPIClient::setPath(const QString path)
{
    _path = path;
}

void ThingpleAbstractAPIClient::setPort(int port)
{
    _port = port;
}

void ThingpleAbstractAPIClient::setCredential(const QString client_id, const QString client_secret)
{
    _client_id = client_id;
    _client_secret = client_secret;
}

void ThingpleAbstractAPIClient::setReaderName(const QString reader_name)
{
    _reader_name = reader_name;
}

QByteArray ThingpleAbstractAPIClient::request(QString method, QString resource, QByteArray data)
{
    qDebug() << "ThingpleAbstractAPIClient::request" << method << resource << " on " << this->_hostname;

    /**
     * Get timestamp
     */
    QDateTime time;
    uint timestamp = time.toTime_t();
    QString timestampStr = "";
    timestampStr.sprintf("%lld", (long long int)timestamp);

    /**
     * Generate random number
     */
    int random = rand() % 1000;
    QString randomStr = "";
    randomStr.sprintf("%d", random);

    /**
     * Generate MD5
     */
    QByteArray md5 = QCryptographicHash::hash(data, QCryptographicHash::Md5).toHex();


    /**
     * Generate content string
     */
    QByteArray content;

    content.append(method.toLatin1()).append("\n");
    content.append(md5).append("\n");
    content.append("application/json").append("\n");
    content.append(timestampStr).append("\n");
    content.append(randomStr).append("\n");
    content.append(resource).append("\n");

    qDebug() << content;

    /**
     * Generate HMAC SHA256
     */
    QByteArray client_secret;
    client_secret.append(this->_client_secret);

    QMessageAuthenticationCode hmac(QCryptographicHash::Sha256);

    hmac.setKey(client_secret);
    hmac.addData(content);
    QByteArray basicAuth = "Thingple " + this->_client_id.toLatin1() + ":" + hmac.result().toHex().toBase64();

    /**
     * Process request
     */
    QString url = "http://" + this->_hostname + this->_path + resource;

    QEventLoop eventLoop;

    QNetworkAccessManager mgr;
    QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit()));

    QNetworkRequest req;

    req.setUrl(QUrl(url));

    QString size = QString::number(data.size());

    req.setRawHeader("Host", this->_hostname.toLatin1());
    req.setRawHeader("Content-Type", "application/json");
    req.setRawHeader("Content-MD5", md5);
    req.setRawHeader("Nonce", randomStr.toLatin1());
    req.setRawHeader("UTC-Timestamp", timestampStr.toLatin1());
    req.setRawHeader("Content-Length", size.toLatin1());
    req.setRawHeader("Authorization", basicAuth);

    QNetworkReply *reply;
    if(data.size() > 0)
    {
        reply = mgr.post(req, data);
    }
    else
    {
        reply = mgr.get(req);
    }

    eventLoop.exec();

    if(reply->error() == QNetworkReply::NoError) {

        QByteArray result = reply->readAll();
        qDebug() << "Success: " << result;

        // Success
        delete reply;
        return result;
    }
    else {
        qDebug() << "ThingpleAbstractAPIClient::request Error!" << reply->errorString();

        qDebug() << reply->readAll();
        // Error
        delete reply;
        return QByteArray();
    }
}

QByteArray ThingpleAbstractAPIClient::get(QString resource)
{
    QByteArray result = this->request("GET", resource, QByteArray());
    return result;
}

QByteArray ThingpleAbstractAPIClient::post(QString resource, QByteArray data)
{
    QByteArray result = this->request("POST", resource, data);
    return result;
}
